﻿using System;
using System.Collections.Generic;

using MxNetwork;
using GameTools;
using System.Collections;

namespace DTFight
{
	public class GameData
	{
		static public void Read(ref MxNwkPacket pkt, GameData data)
		{
			pkt.Write ((UInt32)data.CurStateInfo.StateFlag);
			pkt.Write (data.CurStateInfo.WaiteTime);
			pkt.Write ((DateTime.Now - data.CurStateInfo.StartTime).Ticks);

			foreach (var card in data.DesktopCards)
			{
				if (card == null)
					pkt.Write((byte)0);
				else
				{
					pkt.Write(card.CardId);
				}
			}

			// history
			pkt.Write ((byte)data.ResultList.Count);
			foreach (var info in data.ResultList) {
				ResultInfo.Read (ref pkt, info);
			}
		}

		static public void Write(ref MxNwkPacket pkt, ref GameData data)
		{
			data.CurStateInfo.StateFlag = (STATE_FLAG)pkt.ReadUInt32 ();
			data.CurStateInfo.WaiteTime = pkt.ReadFloat ();
			data.CurStateInfo.StartTime = DateTime.Now - TimeSpan.FromTicks (pkt.ReadInt64());

			for (int i=0; i<data.DesktopCards.Length; ++i)
			{
				byte cardId = pkt.ReadUInt8();
				if (cardId == 0)
					data.DesktopCards[i] = null;
				else
					data.DesktopCards[i] = CardData.CardPool[cardId - 1];
			}

			// history
			data.ResultList.Clear();
			int count = pkt.ReadUInt8 ();
			for (int i = 0; i < count; ++i) {
				ResultInfo rt = new ResultInfo ();
				ResultInfo.Write (ref pkt, ref rt);
				data.ResultList.Add (rt);
			}
		}

		public const UInt32 BASE_POINT = 500;
		public const int HISTORY_COUNT = 10;

		// 玩家
		public Dictionary<string, PlayerData> TotalPlayers = new Dictionary<string, PlayerData> ();
		public int PlayerCount
		{
			get { return TotalPlayers.Count; }
		}

		public void PlayerEnter(PlayerData player)
		{
			if (TotalPlayers.ContainsKey (player.PlayerId)) {
				TotalPlayers [player.PlayerId] = player;
				return;
			}
			TotalPlayers.Add (player.PlayerId, player);
		}

		public bool PlayerLeave(string playerId)
		{
			return TotalPlayers.Remove (playerId);
		}

		// 分组
		public enum GROUP_TYPE
		{
			龙潭,
			虎穴,

			MAX
		}

		public class GroupInfo
		{
			public GROUP_TYPE Group = GROUP_TYPE.MAX;
			public UInt32 TotalPoints = 0;
			public UInt32 TotalBetPoint = 0;

			public HashSet<PlayerData> PlayerList = new HashSet<PlayerData>();
			public int PlayerCount
			{
				get { return PlayerList.Count; }
			}

			public void Reset()
			{
				TotalPoints = 0;
				TotalBetPoint = 0;
				PlayerList.Clear ();
			}
		}

		public GroupInfo[] GroupInfos = new GroupInfo[(int)GROUP_TYPE.MAX];
		public GroupInfo GpDragon
		{
			get { return GroupInfos [(int)GROUP_TYPE.龙潭]; }
		}
		public GroupInfo GpTiger
		{
			get { return GroupInfos [(int)GROUP_TYPE.虎穴]; }
		}

		// 游戏状态
		public enum STATE_FLAG
		{
			SELECT_GROUP,
			OPEN_NONE,
			OPEN_CARD_1,
			OPEN_CARD_2,
			OPEN_CARD_3,
			OPEN_CARD_4,
			SHOW_BET_RESULT,

			MAX
		}

		public struct StateInfo
		{
			public STATE_FLAG StateFlag;
			public float WaiteTime;
			public DateTime StartTime;
		}

		static public float[] WaiterTime = new float[(int)STATE_FLAG.MAX];
		public StateInfo CurStateInfo = new StateInfo();
		public List<byte> RemainCards = new List<byte> ();
		public CardData[] DesktopCards = new CardData[(int)CardData.CARD_TYPE.MAX];

		// 结算信息
		public struct PlayerBetResult
		{
			public PlayerData Player;
			public int ResultPoint;
		}

		public class ResultInfo
		{
			public struct GroupResult
			{
				public int PlayerCount;
				public byte RightCardNum;
				public byte FinalCardPoint;
			}

			public GROUP_TYPE WinGroup = GROUP_TYPE.MAX;
			public byte[] Cards = new byte[(int)CardData.CARD_TYPE.MAX];
			public GroupResult[] GroupResults = new GroupResult[(int)GROUP_TYPE.MAX];

			// bet players
			public List<PlayerBetResult> PlayerBetResults;

			static public void Read(ref MxNwkPacket pkt, ResultInfo data)
			{
				pkt.Write ((UInt32)data.WinGroup);

				foreach (var card in data.Cards) {
					pkt.Write (card);
				}
			}

			static public void Write(ref MxNwkPacket pkt, ref ResultInfo data)
			{
				data.WinGroup = (GROUP_TYPE)pkt.ReadUInt32 ();

				for (int j = 0; j < data.Cards.Length; ++j) {
					data.Cards [j] = pkt.ReadUInt8();
				}
			}
		}

		public List<ResultInfo> ResultList = new List<ResultInfo> ();

		public void AddResultInfo(ResultInfo result)
		{
			if (ResultList.Count == HISTORY_COUNT) {
				ResultList.RemoveAt (0);
			}

			ResultList.Add (result);

			if (ResultList.Capacity > HISTORY_COUNT)
				ResultList.TrimExcess ();
		}

		public bool SendBroadcast { get; set; }
		public void Init()
		{
			for (int i = 0; i < GroupInfos.Length; ++i) {
				GroupInfo group = new GroupInfo ();
				group.Group = (GROUP_TYPE)i;
				GroupInfos [i] = group;
			}

			WaiterTime [(int)STATE_FLAG.SELECT_GROUP] = 30;
			WaiterTime [(int)STATE_FLAG.OPEN_NONE] = 15;
			WaiterTime [(int)STATE_FLAG.OPEN_CARD_1] = 15;
			WaiterTime [(int)STATE_FLAG.OPEN_CARD_2] = 15;
			WaiterTime [(int)STATE_FLAG.OPEN_CARD_3] = 5;
			WaiterTime [(int)STATE_FLAG.OPEN_CARD_4] = 3;
			WaiterTime [(int)STATE_FLAG.SHOW_BET_RESULT] = 5;

			CurStateInfo.StateFlag = STATE_FLAG.MAX;
			CurStateInfo.StartTime = DateTime.Now;
			CurStateInfo.WaiteTime = 0;
			SendBroadcast = false;
		}

		public void Start()
		{
			if (RemainCards.Count < DesktopCards.Length) {
				RemainCards.Clear ();
				for (int i=1; i<=CardData.CardPool.Length; ++i) {
					RemainCards.Add ((byte)i);
				}
			}

			foreach (var groupInfo in GroupInfos) {
				groupInfo.Reset ();
			}

			for (int i = 0; i < DesktopCards.Length; ++i) {
				DesktopCards [i] = null;
			}

			CurStateInfo.StateFlag = STATE_FLAG.SELECT_GROUP;
			CurStateInfo.StartTime = DateTime.Now;
			CurStateInfo.WaiteTime = WaiterTime [(int)STATE_FLAG.SELECT_GROUP];

			SendBroadcast = true;
		}

		public void Over()
		{
			// 结算
			int dragonCardNum = 0;
			int tigerCardNum = 0;
			GROUP_TYPE winGroupType = GetResult (ref dragonCardNum, ref tigerCardNum);

			ResultInfo.GroupResult rtDragon = new ResultInfo.GroupResult ();
			rtDragon.RightCardNum = (byte)dragonCardNum;
			rtDragon.PlayerCount = GpDragon.PlayerCount;
			rtDragon.FinalCardPoint = DesktopCards [(int)CardData.CARD_TYPE.大莲].CardId;

			ResultInfo.GroupResult rtTiger = new ResultInfo.GroupResult ();
			rtTiger.RightCardNum = (byte)dragonCardNum;
			rtTiger.PlayerCount = GpTiger.PlayerCount;
			rtTiger.FinalCardPoint = DesktopCards [(int)CardData.CARD_TYPE.小莲].CardId;

			UInt32 basePoint = 0;
			if (DesktopCards [(int)CardData.CARD_TYPE.神物].CardType == CardData.CARD_TYPE.神物) {
				basePoint = (UInt32)(GpDragon.PlayerCount + GpTiger.PlayerCount) * BASE_POINT;
			}

			ResultInfo rt = new ResultInfo ();
			rt.WinGroup = winGroupType;
			rt.GroupResults [(int)GROUP_TYPE.龙潭] = rtDragon;
			rt.GroupResults [(int)GROUP_TYPE.虎穴] = rtTiger;

			for (int i = 0; i < rt.Cards.Length; ++i) {
				rt.Cards [i] = DesktopCards [i].CardId;
			}

			// 计算玩家赢的点数
			GroupInfo winGroup, loseGroup;
			if (winGroupType == GROUP_TYPE.龙潭) {
				winGroup = GpDragon;
				loseGroup = GpTiger;
			} else {
				winGroup = GpTiger;
				loseGroup = GpDragon;
			}

			List<PlayerBetResult> betRts = new List<PlayerBetResult> ();
			if (winGroup.TotalPoints > 0)
			{
				foreach (var player in winGroup.PlayerList) {
					PlayerBetResult betRt = new PlayerBetResult ();
					betRt.Player = player;

					UInt32 betPoint = 0, realBetPoint = 0;
					for (int i=0; i<player.BetPoints.Length; ++i) {
						betPoint += player.BetPoints[i];
						realBetPoint += (UInt32)(player.BetPoints [i] / (i + 1));
					}

					if (betPoint == 0)
						continue;

					float pst = (float)betPoint / winGroup.TotalPoints;
					betRt.ResultPoint = (int)(pst * loseGroup.TotalBetPoint + 0.5);
					betRt.ResultPoint += (int)realBetPoint;	// return real bet points

					if (basePoint > 0)
						betRt.ResultPoint += (int)(pst * basePoint + 0.5);

					betRts.Add (betRt);
					player.Points += (UInt32)betRt.ResultPoint;
					player.WinPointsInRoom += betRt.ResultPoint;
				}
			}
			rt.PlayerBetResults = betRts;

			AddResultInfo (rt);
		}
			
		public void Update()
		{
			double sec = (DateTime.Now - CurStateInfo.StartTime).TotalSeconds;
			bool nextTurn = sec >= CurStateInfo.WaiteTime;

			if (nextTurn) {

				switch (CurStateInfo.StateFlag)
				{
				case STATE_FLAG.SELECT_GROUP:
					{
						CurStateInfo.StateFlag = STATE_FLAG.OPEN_NONE;
						SendBroadcast = true;
					}
					break;
				case STATE_FLAG.OPEN_NONE:
					{
						// 翻开第1张牌
						CurStateInfo.StateFlag = STATE_FLAG.OPEN_CARD_1;

						DesktopCards[(int)CardData.CARD_TYPE.盛开] = randomCard ();
						DesktopCards[(int)CardData.CARD_TYPE.神仙] = randomCard ();
						SendBroadcast = true;
					}
					break;
				case STATE_FLAG.OPEN_CARD_1:
					{
						// 翻开第2张牌
						CurStateInfo.StateFlag = STATE_FLAG.OPEN_CARD_2;

						DesktopCards[(int)CardData.CARD_TYPE.百花] = randomCard ();
						DesktopCards[(int)CardData.CARD_TYPE.神虎] = randomCard ();
						SendBroadcast = true;
					}
					break;
				case STATE_FLAG.OPEN_CARD_2:
					{
						// 翻开第3张牌
						CurStateInfo.StateFlag = STATE_FLAG.OPEN_CARD_3;

						DesktopCards[(int)CardData.CARD_TYPE.大莲] = randomCard ();
						DesktopCards[(int)CardData.CARD_TYPE.小莲] = randomCard ();
						SendBroadcast = true;
					}
					break;
				case STATE_FLAG.OPEN_CARD_3:
					{
						// 翻开第4张牌
						CurStateInfo.StateFlag = STATE_FLAG.OPEN_CARD_4;

						DesktopCards[(int)CardData.CARD_TYPE.神物] = randomCard ();
						SendBroadcast = true;
					}
					break;
				case STATE_FLAG.OPEN_CARD_4:
					{
						// 显示结果
						CurStateInfo.StateFlag = STATE_FLAG.SHOW_BET_RESULT;
						Over();
						SendBroadcast = true;
					}
					break;
				case STATE_FLAG.SHOW_BET_RESULT:
					{
						// 重新开始
						CurStateInfo.StateFlag = STATE_FLAG.MAX;
						return;
					}
				}

				// next state
				CurStateInfo.StartTime = DateTime.Now;
				CurStateInfo.WaiteTime = WaiterTime[(int)CurStateInfo.StateFlag];
			}
		}

		CardData randomCard()
		{
			int idx = GameHelper.RandomSource.Next (RemainCards.Count);

			CardData data = CardData.CardPool[RemainCards [idx] - 1];
			RemainCards.RemoveAt (idx);

			return data;
		}

		public GROUP_TYPE GetResult(ref int dragonCardNum, ref int tigerCardNum)
		{
			dragonCardNum = 0;
			tigerCardNum = 0;
			for (int i=0; i<(int)CardData.CARD_TYPE.神物; ++i)
			{
				if (DesktopCards [i] == null)
					break;

				if (DesktopCards [i].CardType != (CardData.CARD_TYPE)i)
					continue;

				if (i <= (int)CardData.CARD_TYPE.大莲)
					++dragonCardNum;
				else
					++tigerCardNum;
			}

			GROUP_TYPE group = GROUP_TYPE.MAX;
			if (dragonCardNum > tigerCardNum) {
				group = GROUP_TYPE.龙潭;
			} else if (dragonCardNum < tigerCardNum) {
				group = GROUP_TYPE.虎穴;
			}
			else {
				if (DesktopCards [(int)CardData.CARD_TYPE.大莲] != null && DesktopCards [(int)CardData.CARD_TYPE.小莲] != null) {
					if (DesktopCards [(int)CardData.CARD_TYPE.大莲].CardId > DesktopCards [(int)CardData.CARD_TYPE.小莲].CardId) {
						group = GROUP_TYPE.龙潭;
					} else {
						group = GROUP_TYPE.虎穴;
					}
				}
			}

			return group;
		}

		public GROUP_TYPE GetPlayerGroup(PlayerData player)
		{
			foreach (var groupInfo in GroupInfos) {
				if (groupInfo.PlayerList.Contains (player))
					return groupInfo.Group;
			}

			return GROUP_TYPE.MAX;
		}

		public void SelectGroup(PlayerData player, GROUP_TYPE group)
		{
			if (GetPlayerGroup (player) != GROUP_TYPE.MAX)
				return;

			// add group
			GroupInfos [(int)group].PlayerList.Add (player);
			for (int i=0; i<player.BetPoints.Length; ++i) {
				player.BetPoints[i] = 0;
			}
		}

		public byte BetPoint(PlayerData player, UInt32 points)
		{
			if (player == null)
				return 0;

			if (points > player.Points)
				return 0;

			GroupInfo group = null;
			if (GpDragon.PlayerList.Contains (player))
				group = GpDragon;
			else if (GpTiger.PlayerList.Contains (player))
				group = GpTiger;
			else
				return 0;
				
			byte rate = 0;

			switch (CurStateInfo.StateFlag) {
			case STATE_FLAG.OPEN_NONE:
				{
					rate = 1;
				}
				break;
			case STATE_FLAG.OPEN_CARD_1:
				{
					rate = 2;
				}
				break;
			case STATE_FLAG.OPEN_CARD_2:
				{
					rate = 3;
				}
				break;
			case STATE_FLAG.OPEN_CARD_3:
			case STATE_FLAG.OPEN_CARD_4:
				return 0;
			default:
				return 0;
			}
				
			UInt32 curPoint = points * rate;
			player.BetPoints [rate - 1] += curPoint;
			player.WinPointsInRoom -= (int)points;

			group.TotalPoints += curPoint;
			group.TotalBetPoint += points;

			return rate;
		}

		public enum ADVISER_TYPE
		{
			拉斯维加斯,
			澳门,
			国内,

			MAX
		}

		public GROUP_TYPE QueryAdviser(ADVISER_TYPE adviser)
		{
			if (ResultList.Count == 0)
				return GROUP_TYPE.MAX;

			ResultInfo rtInfo = ResultList [ResultList.Count - 1];
			GROUP_TYPE lastWinGroup = rtInfo.WinGroup;
			GROUP_TYPE group = GROUP_TYPE.MAX;

			switch (adviser) {
			case ADVISER_TYPE.拉斯维加斯:
				{
					// 统计两组牌各种花色的数量
					int[,] cardTypeCounts = new int[(int)GROUP_TYPE.MAX, (int)CardData.CARD_TYPE.MAX];
					for (int i = 0; i < rtInfo.Cards.Length-1; ++i) {
						CardData.CARD_TYPE type = CardData.CardPool [rtInfo.Cards [i] - 1].CardType;

						if (i <= (int)CardData.CARD_TYPE.大莲) {
							cardTypeCounts [(int)GROUP_TYPE.龙潭, (int)type] += 1;
						} else {
							cardTypeCounts [(int)GROUP_TYPE.虎穴, (int)type] += 1;
						}
					}

					// 计算拥有相同花色种类的数量,以及同种花色数量是否相同
					int typeCount = 0;
					bool hasDiffCount = false;
					for (int i = 0; i < (int)CardData.CARD_TYPE.MAX; ++i) {
						if (cardTypeCounts [(int)GROUP_TYPE.龙潭, i] > 0 && cardTypeCounts [(int)GROUP_TYPE.虎穴, i] > 0) {
							++typeCount;

							if (cardTypeCounts [(int)GROUP_TYPE.龙潭, i] != cardTypeCounts [(int)GROUP_TYPE.虎穴, i])
								hasDiffCount = true;
						}
					}

					// 判定顾问结果
					if (typeCount == 0) {
						group = lastWinGroup == GROUP_TYPE.虎穴 ? GROUP_TYPE.龙潭 : GROUP_TYPE.虎穴;

						// 如果最后一组牌是神虎大，下一局押虎穴

					} else if (typeCount == 1 || typeCount == 3) {
						group = GROUP_TYPE.虎穴;
					} else if (typeCount == 2) {
						if (!hasDiffCount && lastWinGroup == GROUP_TYPE.虎穴)
							group = GROUP_TYPE.龙潭;
						else
							group = GROUP_TYPE.虎穴;
					} else {
						group = lastWinGroup == GROUP_TYPE.虎穴 ? GROUP_TYPE.龙潭 : GROUP_TYPE.虎穴;
					}
				}
				break;
			case ADVISER_TYPE.澳门:
				{
					int rightCardCount = rtInfo.GroupResults [(int)GROUP_TYPE.龙潭].RightCardNum
					                     + rtInfo.GroupResults [(int)GROUP_TYPE.虎穴].RightCardNum;

					if (rightCardCount == 0) {
						group = lastWinGroup == GROUP_TYPE.虎穴 ? GROUP_TYPE.龙潭 : GROUP_TYPE.虎穴;

						//龙潭大，下一局押虎穴，但最后一组牌如果是神物、神仙大，那么下一局还是押龙潭。
						//虎穴大，下一局押虎穴，如果是神虎或者百花大，那么，下一局押龙潭。

					} else if (rightCardCount == 1) {
						if (lastWinGroup == GROUP_TYPE.龙潭) {
							if (CardData.CardPool [rtInfo.Cards [(int)CardData.CARD_TYPE.盛开] - 1].CardType == CardData.CARD_TYPE.盛开
								|| CardData.CardPool [rtInfo.Cards [(int)CardData.CARD_TYPE.百花] - 1].CardType == CardData.CARD_TYPE.百花)
								group = GROUP_TYPE.龙潭;
							else
								group = GROUP_TYPE.虎穴;
						} else {
							if (CardData.CardPool [rtInfo.Cards [(int)CardData.CARD_TYPE.神仙] - 1].CardType == CardData.CARD_TYPE.神仙
								|| CardData.CardPool [rtInfo.Cards [(int)CardData.CARD_TYPE.神虎] - 1].CardType == CardData.CARD_TYPE.神虎)
								group = GROUP_TYPE.虎穴;
							else
								group = GROUP_TYPE.龙潭;
						}

					} else if (rightCardCount == 2) {
						group = GROUP_TYPE.虎穴;
					}
					else {
						group = lastWinGroup == GROUP_TYPE.虎穴 ? GROUP_TYPE.龙潭 : GROUP_TYPE.虎穴;
					}
				}
				break;
			case ADVISER_TYPE.国内:
				{
					int num = 0;
					foreach (var info in rtInfo.Cards) {
						num += info;
					}

					group = num > 100 ? GROUP_TYPE.龙潭 : GROUP_TYPE.虎穴;
				}
				break;
			}

			return group;
		}
	}

}